﻿#region Namespace References

using System;
using System.Collections.Generic;
using System.Xml;
using DynConDevCorp.OpenArch.SmartData.Core;
using DynConDevCorp.OpenArch.SmartData.Interfaces;

#endregion

namespace DynConDevCorp.OpenArch.SmartData.Persistance
{
    /// <summary>
    /// 
    /// </summary>
    public class SmartGraphWriterXml : ISmartDispatcher
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SmartGraphWriterXml"/> class.
        /// </summary>
        /// <param name="xmlWriter">The XML writer.</param>
        public SmartGraphWriterXml(XmlWriter xmlWriter)
        {
            m_XmlWriter = xmlWriter;
        }

        #endregion

        #region Public Instance Methods

        /// <summary>
        /// Writes the specified graph.
        /// </summary>
        /// <param name="graph">The graph.</param>
        public void Write(SmartEntityInstanceGraph graph)
        {
            m_ObjectGraph = graph;

            m_XmlWriter.WriteStartElement("SmartObjectGraph");
            WriteEntities();
            WriteCollectionNames();
            WriteCollectionEntities();
            m_XmlWriter.WriteEndElement(); // "SmartObjectGraph"
        }

        #endregion

        #region Private Instance Methods

        private void WriteCollectionEntities()
        {
            m_XmlWriter.WriteStartElement("CollectionContents");
            foreach (string collectionName in m_ObjectGraph.CollectionNames)
            {
                m_XmlWriter.WriteStartElement("Collection");
                {
                    m_XmlWriter.WriteStartElement("Name");
                    m_XmlWriter.WriteValue(collectionName);
                    m_XmlWriter.WriteEndElement(); //"Name"
                }
                {
                    m_XmlWriter.WriteStartElement("Contents");
                    List<Guid> entityIDs = m_ObjectGraph.CollectionEntityIDs(collectionName);
                    foreach (Guid EntityID in entityIDs)
                    {
                        m_XmlWriter.WriteStartElement("EntityID");
                        m_XmlWriter.WriteValue(EntityID.ToString());
                        m_XmlWriter.WriteEndElement(); // "EntityID"
                    }
                    m_XmlWriter.WriteEndElement(); //"Contents"
                }
                m_XmlWriter.WriteEndElement(); //"Collection"
            }
            m_XmlWriter.WriteEndElement(); // "CollectionContents"
        }

        private void WriteCollectionNames()
        {
            m_XmlWriter.WriteStartElement("CollectionNames");
            foreach (string collectionName in m_ObjectGraph.CollectionNames)
            {
                m_XmlWriter.WriteStartElement("Name");
                m_XmlWriter.WriteValue(collectionName);
                m_XmlWriter.WriteEndElement(); // "Name"
            }
            m_XmlWriter.WriteEndElement(); // "CollectionNames"
        }

        private void WriteEntities()
        {
            m_XmlWriter.WriteStartElement("Entities");
            List<ISmartEntity> entities = m_ObjectGraph.Entities();

 
            foreach (SmartEntity entity in entities)
            {
                PersistanceMetadata persistanceMetadata = PersistanceMetadataManager.GlobalInstance.GetMetadata(entity.GetType());
                EntityFieldMap fieldMap = persistanceMetadata.ObjectToXmlMap();
                WriteEntity(fieldMap, entity);
            }
            m_XmlWriter.WriteEndElement(); // "Entities"
        }

        private void WriteEntity(IEnumerable<FieldMapEntry> fieldMap, ISmartEntity entity)
        {
            m_XmlWriter.WriteStartElement("Entity");
            {
                m_XmlWriter.WriteStartElement("MetaEntityTypeName");
                m_XmlWriter.WriteValue(entity.GetType().FullName);
                m_XmlWriter.WriteEndElement(); // "MetaEntityTypeName"
            }
            {
                m_XmlWriter.WriteStartElement("EntityID");
                m_XmlWriter.WriteValue(entity.EntityID.ToString());
                m_XmlWriter.WriteEndElement(); // "EntityID"
            }
              
            WriteEntityFieldValues(fieldMap, entity);
            m_XmlWriter.WriteEndElement(); // "Entity"
        }
        
        //PersistanceMetadata persistanceMetadata = PersistanceMetadataManager.GlobalInstance.GetMetadata(entityType);

        private void WriteEntityFieldValues(IEnumerable<FieldMapEntry> fieldMap, ISmartEntity entity)
        {
            m_XmlWriter.WriteStartElement("FieldValues");
            foreach (FieldMapEntry entry in fieldMap)
            {
                WriteEntityFieldValue(entry, entity);
            }
            m_XmlWriter.WriteEndElement(); // "FieldValues"
        }

        private void WriteEntityFieldValue(FieldMapEntry mapEntry, ISmartEntity entity)
        {
            m_XmlWriter.WriteStartElement(mapEntry.PersistanceFieldName);
            FieldValueContext fieldValueContext = new FieldValueContext();
            entity[mapEntry.ObjectFieldName].Dispatch(this, fieldValueContext);
            object rawValue = fieldValueContext.FieldVaLue;
            if (rawValue != null)
            {
                m_XmlWriter.WriteValue(rawValue.ToString());
            }
            m_XmlWriter.WriteEndElement(); // fieldName
        }

        #endregion

        #region Fields and Constants

        private readonly XmlWriter m_XmlWriter;
        private SmartEntityInstanceGraph m_ObjectGraph;

        #endregion

        #region Implementation of ISmartDispatcher

        void ISmartDispatcher.ProcessDispatch(ISmartEntity target, object context)
        {
            SetFieldValueContext(context, target.EntityID);
        }

        void ISmartDispatcher.ProcessDispatch(ISmartEntityCollection target, object context)
        {
            throw new NotImplementedException();
        }

        void ISmartDispatcher.ProcessDispatch(ISmartEntityLink target, object context)
        {
            SetFieldValueContext(context, target.LinkedEntityID);
        }

        void ISmartDispatcher.ProcessDispatch(ISmartField target, object context)
        {
            SetFieldValueContext(context, target.RawValue);
        }

        void ISmartDispatcher.ProcessDispatch(ISmartNullableField target, object context)
        {
            throw new NotImplementedException();
        }

        void ISmartDispatcher.ProcessDispatch(ISmartDisplayFormatter target, object context)
        {
            throw new NotImplementedException();
        }

        static void SetFieldValueContext(object context, object value)
        {
            FieldValueContext fvc = (FieldValueContext) context;
            fvc.FieldVaLue = value;
        }

        class FieldValueContext
        {
        public object FieldVaLue;
        }
        #endregion
    }
}